package com.listeningframework.boot.autoconfigure.apidoc;

import static com.google.common.collect.Lists.newArrayList;
import static springfox.documentation.schema.ResolvedTypes.modelRefFactory;
import static springfox.documentation.spi.schema.contexts.ModelContext.inputParam;

import java.util.List;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.core.annotation.Order;
import org.springframework.data.domain.Pageable;
import org.springframework.stereotype.Component;

import com.fasterxml.classmate.ResolvedType;
import com.fasterxml.classmate.TypeResolver;
import com.google.common.base.Function;

import springfox.documentation.schema.ModelReference;
import springfox.documentation.schema.TypeNameExtractor;
import springfox.documentation.service.Parameter;
import springfox.documentation.service.ResolvedMethodParameter;
import springfox.documentation.spi.DocumentationType;
import springfox.documentation.spi.schema.contexts.ModelContext;
import springfox.documentation.spi.service.ParameterBuilderPlugin;
import springfox.documentation.spi.service.contexts.ParameterContext;
import springfox.documentation.swagger.common.SwaggerPluginSupport;

@Component
@Order(SwaggerPluginSupport.SWAGGER_PLUGIN_ORDER)
@ConditionalOnProperty(prefix = "listening.apidoc.swagger", name = "enabled", havingValue = "true", matchIfMissing = false)
public class PageableParameterBuilderPlugin implements ParameterBuilderPlugin {
    private final TypeNameExtractor nameExtractor;
    private final TypeResolver      resolver;

    @Autowired
    public PageableParameterBuilderPlugin(TypeNameExtractor nameExtractor, TypeResolver resolver) {
        this.nameExtractor = nameExtractor;
        this.resolver = resolver;
    }

    @Override
    public boolean supports(DocumentationType delimiter) {
        return true;
    }

    private Function<ResolvedType, ? extends ModelReference> createModelRefFactory(ParameterContext context) {
        // @formatter:off
        ModelContext modelContext = inputParam(
            context.resolvedMethodParameter().getParameterType(),
            context.getDocumentationType(), 
            context.getAlternateTypeProvider(), 
            context.getGenericNamingStrategy(),
            context.getIgnorableParameterTypes());
        return modelRefFactory(modelContext, nameExtractor);
        // @formatter:on
    }

    @Override
    public void apply(ParameterContext context) {
        // @formatter:off
        ResolvedMethodParameter parameter = context.resolvedMethodParameter();
        Class<?> type = parameter.getParameterType().getErasedType();
        if (type == null || !Pageable.class.isAssignableFrom(type)) {
            return;
        }
        
        Function<ResolvedType, ? extends ModelReference> factory = createModelRefFactory(context);
        ModelReference intModel = factory.apply(resolver.resolve(Integer.TYPE));
        ModelReference stringModel = factory.apply(resolver.resolve(List.class, String.class));
        List<Parameter> parameters = newArrayList(
            context.parameterBuilder()
                   .parameterType("query")
                   .name("page")
                   .modelRef(intModel)
                   .description("页码").build(),
            context.parameterBuilder()
                   .parameterType("query")
                   .name("size")
                   .modelRef(intModel)
                   .description("页面大小").build(),
            context.parameterBuilder()
                   .parameterType("query")
                   .name("sort")
                   .modelRef(stringModel)
                   .allowMultiple(true)
                   .description("排序(支持多条件组合)").build()
        );
        context.getOperationContext().operationBuilder().parameters(parameters);
     // @formatter:on
    }

}
